package graph.editor;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RectangularShape;
import java.io.File;
import java.util.Observer;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.JToolBar;

import observable.ObservableAutomaton;
import observable.ObserverImpl;

import util.StringToStringArray;
import automaton.TypeState;
import exception.LabelException;
import graph.editor.enhanced.GraphComponentModified;

/**
 * @depend - - - util.StringToStringArray
 * @depend - - - exception.LabelException
 * @depend - - - graph.editor.DataReader
 * @depend - - - graph.editor.DataWriter
 * @depend - - - graph.editor.DataToAutomaton
 * @depend - - - graph.editor.Edge
 * @depend - - - graph.editor.GraphEditor
 * @depend - - - graph.editor.Vertex
 * @depend - - - automaton.TypeState
 */
public class GraphFrame extends JFrame {
	private static final long serialVersionUID = 1L;
	GraphComponentModified component;
	private FramesController controller;
	
	JTextField patternField;

	public GraphFrame(FramesController controller) {
		this.controller = controller;

		component = new GraphComponentModified();
		component.setForeground(Color.BLACK);
		component.setBackground(Color.WHITE);
		component.setOpaque(true);
		component.setPreferredSize(new Dimension(1000, 1000));
		JScrollPane scrollPane = new JScrollPane(component);

		JMenuBar menuBar = new JMenuBar();
		setJMenuBar(menuBar);
		JMenu menu = new JMenu(GraphEditor.MENU_FILE);
		menuBar.add(menu);
		createMenuItem(menu, GraphEditor.MENU_ITEM_NEW, new ActionListener() {
			public void actionPerformed(ActionEvent ev) {
				GraphFrame.this.controller.createFrame();
			}
		});
		createMenuItem(menu, GraphEditor.MENU_ITEM_CLOSE, new ActionListener() {
			public void actionPerformed(ActionEvent ev) {
				GraphFrame.this.controller.deleteFrame(GraphFrame.this);
			}
		});
		
		createMenuSeparator(menu);
		
		createMenuItem(menu, GraphEditor.MENU_ITEM_SAVE, new ActionListener() {
			public void actionPerformed(ActionEvent ev) {
				Component parent = (Component) ev.getSource();
				JFileChooser fileChooser = new JFileChooser(System.getProperty("user.home"));
				int status = fileChooser.showSaveDialog(parent);
				if (status == JFileChooser.APPROVE_OPTION) {
					File selectedFile = fileChooser.getSelectedFile();
					DataWriter dataWriter = new DataWriter(component.getEdges(), selectedFile);
					dataWriter.save();
				}
				else if (status == JFileChooser.CANCEL_OPTION) {
					// nothing TODO
				}
			}
		});
		
		createMenuItem(menu, GraphEditor.MENU_ITEM_LOAD, new ActionListener() {
			public void actionPerformed(ActionEvent ev) {
				Component parent = (Component) ev.getSource();
				JFileChooser fileChooser = new JFileChooser(System.getProperty("user.home"));
				int status = fileChooser.showOpenDialog(parent);
				if (status == JFileChooser.APPROVE_OPTION) {
					File xmlFile = fileChooser.getSelectedFile();
					DataReader dataReader = new DataReader(xmlFile);
					dataReader.load();
					component.setVertices(dataReader.getVertices());
					component.setEdges(dataReader.getEdges());
					component.addTextField();
					component.refresh();
				}
				else if (status == JFileChooser.CANCEL_OPTION) {
					// nothing TODO
				}
			}
		});
		
		
		createMenuSeparator(menu);
		createMenuItem(menu, GraphEditor.MENU_ITEM_QUIT, new ActionListener() {
			public void actionPerformed(ActionEvent ev) {
				GraphFrame.this.controller.quit();
			}
		});

		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent e) {
				GraphFrame.this.controller.deleteFrame(GraphFrame.this);
			}
		});
		
		JToolBar toolbar = new JToolBar();
		toolbar.setLayout(new GridLayout(6, 1));
		addShapeButton(toolbar, new Rectangle2D.Double(0, 0, 10, 10), "Initial State", TypeState.INITIAL);
		addShapeButton(toolbar, new Rectangle2D.Double(0, 0, 15, 15), "Initial/Final State", TypeState.INITIAL_AND_FINAL);
		addShapeButton(toolbar, new Rectangle2D.Double(0, 0, 20, 20), "Final State", TypeState.FINAL);
		addShapeButton(toolbar, new Ellipse2D.Double(0, 0, 10, 10), "Intermediate State", TypeState.INTERMEDIATE);
		addEntryField(toolbar, "Pattern");
		try {
			addButton(toolbar,  "Verify");
		} catch (LabelException e1) {
			showMessageDialog("WARNING: you can put only one initial state",JOptionPane.WARNING_MESSAGE);
			e1.printStackTrace();
		}

		Container contentPane = getContentPane();
		contentPane.add(toolbar, BorderLayout.WEST);
		contentPane.add(scrollPane, BorderLayout.CENTER);
	}


	public void showMessageDialog(String message, int type){
		switch (type) {
		case JOptionPane.WARNING_MESSAGE:
			JOptionPane.showMessageDialog(this, message,"avertissement",JOptionPane.WARNING_MESSAGE);
			break;
		}
	}
	
	
	private void addShapeButton(JToolBar toolbar, final RectangularShape sample, String name, final TypeState state) {
		JButton button = new JButton(name);
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (component.getInitialVertex()!=null && (state== TypeState.INITIAL || state==TypeState.INITIAL_AND_FINAL)){
					JOptionPane.showMessageDialog(null, "WARNING: you can put only one initial state","avertissement",JOptionPane.WARNING_MESSAGE);
				}
				else{
					component.setVertexSample(sample, state);
				}
			}
		});
		toolbar.add(button);
	}
	
	private void addButton(JToolBar toolbar, String name)  throws LabelException{
		JButton button = new JButton(name);
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				try {
					DataToAutomaton dataToAutomaton = new DataToAutomaton(component.getEdges());
					ObservableAutomaton<String> automate = dataToAutomaton.getObservableAutomaton();
					Observer nouvelObserver = new ObserverImpl ();
					automate.addObserver(nouvelObserver);
					String[] word = StringToStringArray.convert(patternField.getText());
					if (automate.recognize(word))
						JOptionPane.showMessageDialog(null, "Your pattern has been recognized", "Pattern: " + "\"" + patternField.getText() + "\"" + "is valid", JOptionPane.INFORMATION_MESSAGE);
					else
						JOptionPane.showMessageDialog(null, "Your pattern hasn't been recognized", "Pattern: " + "\"" + patternField.getText() + "\"" + "is not valid", JOptionPane.INFORMATION_MESSAGE);
				} catch (LabelException exception) {
					JOptionPane.showMessageDialog(null, "WARNING: you need to fill all the labels","avertissement",JOptionPane.WARNING_MESSAGE);
					exception.printStackTrace();
				}
			}
		});
		toolbar.add(button);
	}
	
	private void addEntryField(JToolBar toolbar, final String name) {
		patternField = new JTextField("set pattern here!");
		patternField.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				JOptionPane.showMessageDialog(null, "Pattern: " + patternField.getText());
			}
		});
		patternField.addMouseListener(new MouseAdapter(){
			public void mouseEntered(MouseEvent e) {
				if(patternField.getText().equals("set pattern here!"))
					patternField.setText("");
			}
			public void mouseExited(MouseEvent e) {
				if(patternField.getText().equals(""))
					patternField.setText("set pattern here!");
			}
		});
		toolbar.add(patternField);
	}

	private void createMenuItem(JMenu menu, String name, ActionListener action) {
		JMenuItem menuItem = new JMenuItem(name);
		menuItem.addActionListener(action);
		menu.add(menuItem);
	}

	private void createMenuSeparator(JMenu menu) {
		JSeparator separator = new JSeparator();
		separator.setForeground(Color.lightGray);
		menu.add(separator);
	}
}
